/*
	PlAr is Platform Arena: a 2D multiplayer shooting game
	Copyright (c) 2010, Antonio Ragagnin <spocchio@gmail.com>
    All rights reserved.

    This file is licensed under the New BSD License.
*/


package plar.core;

import java.util.ArrayList;

import java.util.HashMap;
import java.util.Comparator;

import org.jbox2d.common.*;
import org.jbox2d.dynamics.*;
import org.jbox2d.collision.*;
import org.jbox2d.collision.shapes.*;

/**
 * Element should be a generic element in the Level.
 *
 * Creating an element you don't care about how JBox2D works. you just set the
 * shape, the position, density, friction, the sprites, ecc.
 *
 * @author Antonio Ragagnin
 *
 */
public class Element  implements Comparable {

    public int compareTo(Object e1) {

        return id-(((Element)e1).id);
    }


    public ActionManager actions;
    public HashMap<String, String> properties;
    public Level level;
    public int id=0;
    public Common.ElementType type;
    // if Element.isRunnable the run() function will be called.
    public boolean isRunnable = false;
    public BodyDef bodydef;
    public Body body;
    public PolygonShape shape;
    public SpriteSet currentSpriteSet;
    public ArrayList<Element> collisionList;
    public float friction=0f;
    public float density=1f;
    public float restitution=0f;
    public float lifeTime = -1;
    public float timeGap;
    public AABB currentAABB = null;

    public Element() {
        actions = new ActionManager(this);
        properties = new HashMap<String, String>();
        type = Common.ElementType.NONE;
        setStaticSprite("res/element.png");
        bodydef = new BodyDef();
        actions.addAction("destroy", new ActionDestroy());
        actions.addAction("lifeTimer", new ActionLifeTimer());


    }


    public void setBodyDef(BodyDef bd)
    {
        bd.type = BodyType.STATIC;
    }

    /**
     * called when the element is added to the level and the body is created in
     * the physics layer
     *
     * @param bCamminare sul pavimento
     */

    public void setBody(Body b) {
        b.setUserData(this);
        b.setFixedRotation(true);
        FixtureDef fd = new FixtureDef();
        setFixtureDef(fd);
        setFixture(b.createFixture(fd));

    }

    public void setFixtureDef(FixtureDef fd)
    {
        fd.shape = shape;
    }



    /**
     * called when the element is added to the level and the fixture is created
     * by default from setBody();
     *
     * @param b
     */
    public void setFixture(Fixture f) {
        f.setFriction(friction);
        f.setDensity(density);
        f.setRestitution(restitution);

    }

    public SpriteSet getCurrentSpriteSet() {

        return currentSpriteSet;
    }



    public AABB updateAABB() {
        Vec2 lower = new Vec2();
        Vec2 upper = new Vec2();
        for (Vec2 v : shape.getVertices()) {
            if (v.x < lower.x)
                lower.x = v.x;
            if (v.y < lower.y)
                lower.y = v.y;
            if (v.x > upper.x)
                upper.x = v.x;
            if (v.y > upper.y)
                upper.y = v.y;
            // Common.info(7, "updateAAB():: "+this+" v="+v+" lower+"+lower+" upper+"+upper);
        }

        currentAABB = new AABB(lower, upper);
        //Common.info(7, "updateAAB():: "+currentAABB.lowerBound+" "+currentAABB.upperBound);
        //Common.info(7," new AABB"+currentAABB);
        return currentAABB;
    }

    public AABB getAABB() {

        if (currentAABB == null)
            return updateAABB();

        return currentAABB;
    }

    public Vec2 getSize() {

        Vec2 v= getAABB().getExtents();
        v.x*=2;
        v.y*=2;
        return v;
    }

    public Vec2 getPosition() {
        // return bodydef.position;
        if(body!=null)	return body.getWorldCenter().clone();
        else
        {
            Common.info(1,"Element "+this+" has no body!");
            return null;
        }
    }

    public void setPosition(float x, float y) {

        bodydef.position = new Vec2(x, y);
    }

    public Vec2 getSpeed() {
        return body.getLinearVelocity().clone();
    }

    public Object getProp(String s) {
        return properties.get(s);
    }
    /*
    	public void setSpeed(Vec2 v) {
    		Vec2 cv = getSpeed();
    		Vec2 dv = getSpeed();
    		dv.x = -cv.x + v.x;
    		dv.y = -cv.y + v.y;
    		if (v.x == 0)
    			dv.x = 0;
    		if (v.y == 0)
    			dv.y = 0;
    		applySpeed(dv);
    	}*/
    public void applySpeed(Vec2 dv) {
        if(bodydef.type == BodyType.DYNAMIC)
        {
            /*Vec2 m = new Vec2(body.getMass() * dv.x, body.getMass() * dv.y);
            applyImpulse(m);*/
            float dt = level.Dt; //dt of your world.step(dt);
            float m = body.getMass(); //mass of your body
            Vec2 deltaF = new Vec2(m*dv.x/dt,m*dv.y/dt);
            body.applyForce(deltaF, body.getWorldCenter());
        }
        else if(bodydef.type == BodyType.KINEMATIC)
        {
            bodydef.linearVelocity = dv;
        }
    }




    public void setSpeed(Vec2 targetV) {
        //mz'' = m*c => z' = ct + z'0 => Dz' =  cDT
        Vec2 currentV = body.getLinearVelocity();

        Vec2 deltaV = new Vec2(targetV.x-currentV.x,targetV.y-currentV.y);
        if(targetV.x==0) deltaV.x=0;
        if(targetV.y==0) deltaV.y=0;
        applySpeed(deltaV);


    }

    public void applyForce(Vec2 v) {

        //	Common.info(7, "apply impulse " + v+ " to "+this);
        body.applyLinearImpulse(v, body.getWorldCenter());

    }
    public void applyImpulse(Vec2 v) {

        //	Common.info(7, "apply impulse " + v+ " to "+this);
        body.applyLinearImpulse(v, body.getWorldCenter());
//		this.applySpeed(new Vec2(0f, generator.nextFloat() ));
    }



    public void setBoxShape(float x, float y) {
        shape = new PolygonShape();
        ((PolygonShape) shape).setAsBox(x/2, y/2);
    }

    public void run() {
        actions.runAll();
    }

    public void setProp(String s, String o) {

        properties.put(s, o);
    }

    public void setStaticSprite(String str) {
        currentSpriteSet = new SpriteSet();
        currentSpriteSet.set(str);
    }

    public float getAngle() {
        return body.getAngle();
    }


}
